package worker

import (
	"context"
	"fmt"
	"gogame/logger"
	"reflect"
	"strings"
	"sync"
	"unicode"
	"unicode/utf8"
)

type methodType struct {
	sync.Mutex // protects counters
	method     reflect.Method
	ArgType    reflect.Type
	ResType    reflect.Type
}

var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfContext = reflect.TypeOf((*context.Context)(nil)).Elem()

// isExportedOrBuiltinType 是否可导出(大写)
func isExportedOrBuiltinType(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}

// isExported 是否可导出(大写)
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// DispatchMethods 反射注册接口
func DispatchMethods(service *ApiService, moduleName string, typ reflect.Type) map[string]*methodType {
	methods := make(map[string]*methodType)
	for m := 0; m < typ.NumMethod(); m++ {
		method := typ.Method(m)
		// 已通过func注册
		if service.funcMethod != nil {
			_handle := service.funcMethod[method.Name]
			if _handle != nil {
				continue
			}
		}

		//logger.Print("检测注册【%s.%s】接口", moduleName, method.Name)
		mType := method.Type
		// 方法必须是可导出的(首字母大写)
		if method.PkgPath != "" {
			continue
		}
		// 需要4个参数: receiver, *request, *args, *res.
		if mType.NumIn() != 4 {
			continue
		}

		// 第1个参数必须是 *rpcx.Request
		_requestType := mType.In(1)
		if _requestType.String() != "*rpcx.Request" {
			continue
		}

		// 第2个参数必须是可导出的(首字母大写)
		argType := mType.In(2)
		if !isExportedOrBuiltinType(argType) {
			continue
		}

		resType := mType.In(3)
		// 第3个参数必须是 *rpcx.Request
		if resType.String() != "*rpcx.Response" {
			continue
		}
		// 第3个参数必须是指针
		if resType.Kind() != reflect.Ptr {
			continue
		}
		// 第3个参数必须是可导出的(首字母大写)
		if !isExportedOrBuiltinType(resType) {
			continue
		}

		// 接口check方法不检测
		if strings.Contains(method.Name, "Check") {
			continue
		}

		// 检测对应接口的check是否定义，未定义抛出日志
		_checkFuncName := method.Name + "Check"
		if _, ok := typ.MethodByName(_checkFuncName); !ok {
			logger.WorkerLog.Warn(fmt.Sprintf("接口【%s.%s】未定义check方法!!", moduleName, method.Name))
		}

		methods[method.Name] = &methodType{
			method:  method,
			ArgType: argType,
			ResType: resType,
		}
		// 设置接口注册类型
		service.SetRegisterType(method.Name, "reflect")
		//logger.Print("接口【%s.%s】注册成功", moduleName, method.Name)

		//// arg和res入池  没必要，rpcx已经处理
		//reflectTypePools.Init(argType)
		//reflectTypePools.Init(resType)
	}
	return methods
}
